package com.zhuangjie.rider.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhaungjie.base.utils.PropertiesCopyUtils.PropertiesCopy;
import com.zhuangjie.base.enums.VerificationCodeScenes;
import com.zhuangjie.mvc.future.ContextAwareExecutor;
import com.zhuangjie.redis.service.PhoneCodeRedisService;
import com.zhuangjie.rider.constant.RiderSettingsConstant;
import com.zhuangjie.rider.dto.RiderPasswordLoginDTO;
import com.zhuangjie.rider.dto.RiderPhoneCodeLoginDTO;
import com.zhuangjie.rider.dto.RiderSignUpDTO;
import com.zhuangjie.rider.entity.Rider;
import com.zhuangjie.rider.entity.RiderSettings;
import com.zhuangjie.rider.entity.RiderWorkRegion;
import com.zhuangjie.rider.enums.DeliveryOrderState;
import com.zhuangjie.rider.enums.RiderAccountStatus;
import com.zhuangjie.rider.enums.RiderType;
import com.zhuangjie.rider.enums.RiderWorkState;
import com.zhuangjie.rider.helper.RiderModuleTokenDataProvider;
import com.zhuangjie.rider.mapper.RiderMapper;
import com.zhuangjie.rider.pojo.RiderBasicInfo;
import com.zhuangjie.rider.pojo.SystemSettings;
import com.zhuangjie.rider.service.RiderService;
import com.zhuangjie.rider.service.RiderSettingsService;
import com.zhuangjie.rider.service.RiderWorkRegionService;
import com.zhuangjie.rider.vo.CommonInfosVO;
import com.zhuangjie.rider.vo.RiderWorkStatisticalDataVO;
import com.zhuangjie.security.entity.AuthenticationTokens;
import com.zhuangjie.security.helper.JwtHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhuangjie
 * @since 2023-12-26
 */
@Service
public class RiderServiceImpl extends ServiceImpl<RiderMapper, Rider> implements RiderService {
    @Autowired
    private PhoneCodeRedisService phoneCodeRedisService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RiderWorkRegionService riderWorkRegionService;

    @Autowired
    private RiderSettingsService riderSettingsService;

    @Autowired
    private JwtHelper jwtHelper;

    @Autowired
    private ContextAwareExecutor contextAwareExecutor;




    @Override
    @Transactional
    public void riderRegister(RiderSignUpDTO riderSignUpDTO) throws Exception {
        // 验证码-验证
        phoneCodeRedisService.checkCode(VerificationCodeScenes.RIDER_REGISTER,riderSignUpDTO.getPhoneNumber(),riderSignUpDTO.getCode());
        //  判断账号是否被注册
        LambdaQueryWrapper<Rider> queryRiderWrapper = Wrappers.lambdaQuery(Rider.class)
                .eq(Rider::getPhoneNumber, riderSignUpDTO.getPhoneNumber());
        if (this.baseMapper.selectCount(queryRiderWrapper)>0) {
            throw new RuntimeException("账号已存在");
        }
        // TODO 如果身份证相同，可以考虑禁止再注册
        // 数据组装
        Rider rider = PropertiesCopy.type(RiderSignUpDTO.class, Rider.class)
                .config(c->c.valueAdapter(RiderSignUpDTO::getPassword,source->passwordEncoder.encode(source.getPassword())))
                .produce(riderSignUpDTO);
        if (Objects.equals(RiderType.STAFF,riderSignUpDTO.getType())) {
            throw new RuntimeException("暂不支持专送注册。");
        }
        rider.setType(riderSignUpDTO.getType());
        rider.setState(RiderAccountStatus.NORMAL);
        rider.setWorkStatus(RiderWorkState.OFFLINE);
        // TODO 前端可以考虑将身份证号传入
        // 注册保存rider对象
        this.save(rider);
        // 保存工作区域
        RiderWorkRegion riderWorkRegion = PropertiesCopy.type(RiderSignUpDTO.class, RiderWorkRegion.class)
                .produce(riderSignUpDTO);
        riderWorkRegion.setRiderId(rider.getId());
        riderWorkRegionService.save(riderWorkRegion);
        // 设置默认骑手设置
        RiderSettings riderSettings = new RiderSettings();
        riderSettings.setRiderId(rider.getId());
        riderSettings.setMaxTakingOrder(RiderSettingsConstant.DEFAULT_MAX_TAKING_ORDER);
        riderSettings.setSystemDispatch(RiderSettingsConstant.DEFAULT_SYSTEM_DISPATCH);
        riderSettingsService.save(riderSettings);
    }

    @Override
    public AuthenticationTokens riderPasswordLogin(RiderPasswordLoginDTO riderPasswordLoginDTO) throws Exception {
        Rider rider = this.login(riderPasswordLoginDTO.getPhoneAreaCode(),riderPasswordLoginDTO.getPhoneNumber());
        if (! passwordEncoder.matches(riderPasswordLoginDTO.getPassword(),rider.getPassword())){
            throw new RuntimeException("密码错误！");
        }
        return genRiderAuthenticationTokens(rider.getId());
    }

    private Rider login(String phoneAreaCode,String phoneNumber) {
        LambdaQueryWrapper<Rider> queryRiderWrapper = Wrappers.lambdaQuery(Rider.class)
                .eq(Rider::getPhoneAreaCode, phoneAreaCode)
                .eq(Rider::getPhoneNumber, phoneNumber);
        Rider rider = this.getOne(queryRiderWrapper);

        if (rider == null) {
            throw new RuntimeException("账号不存在！");
        }else if ( RiderAccountStatus.DISABLE.equals(rider.getState())) {
            throw new RuntimeException("账号已被冻结");
        }
        return rider;
    }

    @Override
    public AuthenticationTokens riderPhoneCodeLogin(RiderPhoneCodeLoginDTO riderPhoneCodeLoginDTO) throws Exception {
        // 校验手机验证码
        phoneCodeRedisService.checkCode(VerificationCodeScenes.RIDER_LOGIN, riderPhoneCodeLoginDTO.getPhoneNumber(), riderPhoneCodeLoginDTO.getCode());
        // 检验成功->免密登录
        Rider rider = login(riderPhoneCodeLoginDTO.getPhoneAreaCode(), riderPhoneCodeLoginDTO.getPhoneNumber());
        return genRiderAuthenticationTokens(rider.getId());
    }

    /**
     * 获取骑手基本信息
     *
     * @return {@link RiderBasicInfo}
     */
    @Override
    public RiderBasicInfo getRiderBasicInfo() {
        Long currentRiderId = RiderModuleTokenDataProvider.getCurrentRiderId();
        Rider rider = this.baseMapper.selectById(currentRiderId);
        if (rider == null){
            throw new RuntimeException("骑手不存在！");
        }
        return PropertiesCopy.type(Rider.class, RiderBasicInfo.class)
                .produce(rider);
    }

    @Override
    public void changeWorkStatus(Short workStatus) {
        Long currentRiderId = RiderModuleTokenDataProvider.getCurrentRiderId();
        LambdaUpdateWrapper<Rider> updateRiderWorkStatus = Wrappers.lambdaUpdate(Rider.class)
                .eq(Rider::getId, currentRiderId)
                .set(Rider::getWorkStatus, workStatus);
        if (! this.update(updateRiderWorkStatus)) {
            throw new RuntimeException("修改工作状态失败！");
        }
    }

    @Override
    public CommonInfosVO commonInfos() throws ExecutionException, InterruptedException {
        // 数据请求
        CompletableFuture<RiderBasicInfo> riderBasicInfoCompletableFuture = CompletableFuture.supplyAsync(() -> getRiderBasicInfo(),contextAwareExecutor);
        CompletableFuture<RiderSettings> riderSettingsCompletableFuture = CompletableFuture.supplyAsync(() -> getRiderSettings(),contextAwareExecutor);
        CompletableFuture.allOf(riderBasicInfoCompletableFuture,riderSettingsCompletableFuture).join();
        // 数据组装 并 返回
        return CommonInfosVO.builder()
                .info(riderBasicInfoCompletableFuture.get())
                .riderSettings(riderSettingsCompletableFuture.get())
                .systemSettings(new SystemSettings())
                .build();
    }

    @Override
    public List<Long> filterNotOnlineRider(List<Long> riderIds) {
        LambdaQueryWrapper<Rider> queryOnlineRiderInIdsWrapper = Wrappers.lambdaQuery(Rider.class)
                .in(Rider::getId, riderIds)
                .eq(Rider::getWorkStatus, RiderWorkState.ONLINE)
                .select(Rider::getId, Rider::getWorkStatus);
        List<Rider> list = this.list(queryOnlineRiderInIdsWrapper);
        if (list == null) return new ArrayList<>();
        return list.stream().map(Rider::getId).collect(Collectors.toList());
    }

    /**
     * 过滤器达到最大订单的骑手
     *
     * @param riderIds         骑手id
     * @param workStatus       工作状态
     * @param ltDeliveryStatus lt交货状态
     * @return {@link List}<{@link Long}>
     */
    @Override
    public List<Long> filterUnavailableRiders(List<Long> riderIds, Short workStatus, DeliveryOrderState ltDeliveryStatus) {
        riderIds = this.baseMapper.filterUnavailableRiders(riderIds,workStatus,ltDeliveryStatus);
        if (riderIds == null) {
            return new ArrayList<>();
        }
        return riderIds;
    }

    @Override
    public RiderWorkStatisticalDataVO riderStatistic() {
        return null;
    }

    @Override
    public AuthenticationTokens refreshToken(String refreshToken) throws Exception {
        // 1. 检查骑手状态
        LambdaQueryWrapper<Rider> queryRiderIsAvailableWrapper = Wrappers.lambdaQuery(Rider.class)
                .eq(Rider::getId, jwtHelper.getTokenOwnerId(refreshToken))
                .ne(Rider::getState, RiderAccountStatus.DISABLE);
        if (this.count(queryRiderIsAvailableWrapper) == 0) {
            throw new RuntimeException("账号已被冻结，无法刷新refreshToken!");
        }
        return jwtHelper.refreshToken(refreshToken);
    }




    /**
     * 生成骑手的身份验证令牌
     *
     * @param riderId 骑手id
     * @return {@link AuthenticationTokens}
     */
    private AuthenticationTokens genRiderAuthenticationTokens(Long riderId) throws Exception {
        // 生成token
        AuthenticationTokens authenticationTokens = jwtHelper.loginAfterCreateToken((userConfigTokenDateKeys) -> {
            Map<String, Object> tokenData = new HashMap<>();
            tokenData.put(userConfigTokenDateKeys.USER_ID_KEY, riderId);
            return tokenData;
        });
        return authenticationTokens;
    }


    /**
     * 获取骑手设置信息
     *
     * @return {@link RiderSettings}
     */
    public RiderSettings getRiderSettings() {
        return riderSettingsService.getOne(Wrappers.lambdaQuery(RiderSettings.class).eq(RiderSettings::getRiderId, RiderModuleTokenDataProvider.getCurrentRiderId()));
    }

}
